package msu.ml.core.xml;

import msu.ml.core.*;
import msu.ml.data.*;
import msu.ml.util.*;
import weka.classifiers.*;
import java.util.*;
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;

/**
 * @author Reginald M Mead
 * @version 1.0
 */
public class ExperimentXmlParser
{
    private HashMap<String, IDataLoaderParser> dataLoaderParsers;
    private HashMap<String, IPreProcessorParser> preProcessorParsers;
    private HashMap<String, IPostProcessorParser> postProcessorParsers;
    private HashMap<String, IDataProviderParser> dataProviderParsers;
    private HashMap<String, IClassifierParser> classifierParsers;

    /**
     * Create a new ExperimentXmlParser
     */
    public ExperimentXmlParser()
    {
        dataLoaderParsers = new HashMap<String, IDataLoaderParser>();
        preProcessorParsers = new HashMap<String, IPreProcessorParser>();
        postProcessorParsers = new HashMap<String, IPostProcessorParser>();
        dataProviderParsers = new HashMap<String, IDataProviderParser>();
        classifierParsers = new HashMap<String, IClassifierParser>();
    }

    /**
     * Add an IDataLoaderParser
     *
     * @param id The id by which this parser can be retrieved
     * @param parser the IDataLoaderParser
     */
    public void addDataLoaderParser(String id, IDataLoaderParser parser)
    {
        dataLoaderParsers.put(id, parser);
    }

    /**
     * Add an IPreProcessorParser
     *
     * @param id The id by which this parser can be retrieved
     * @param parser the IPreProcessorParser
     */
    public void addPreProcessorParser(String id, IPreProcessorParser parser)
    {
        preProcessorParsers.put(id, parser);
    }

    /**
     * Add an IPostProcessorParser
     *
     * @param id The id by which this parser can be retrieved
     * @param parser the IPostProcessorParser
     */
    public void addPostProcessorParser(String id, IPostProcessorParser parser)
    {
        postProcessorParsers.put(id, parser);
    }

    /**
     * Add an IDataProviderParser
     *
     * @param id The id by which this parser can be retrieved
     * @param parser the IDataProviderParser
     */
    public void addDataProviderParser(String id, IDataProviderParser parser)
    {
        dataProviderParsers.put(id, parser);
    }

    /**
     * Add an IClassifierParser
     *
     * @param id The id by which this parser can be retrieved
     * @param parser the IClassifierParser
     */
    public void addClassifierParser(String id, IClassifierParser parser)
    {
        classifierParsers.put(id, parser);
    }

    /**
     * Parse the EXL experiment file specified.
     *
     * @return an Experiment
     */
    public Experiment parse(String location)
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try
        {
            Document doc = XmlUtility.parseXmlFile(location);
            Experiment experiment = new Experiment();

            Element experimentNode = (Element)(doc.getElementsByTagName(
                        "experiment").item(0));

            experiment.setName(experimentNode.getAttribute("label"));

            NodeList nodes = experimentNode.getChildNodes(); 
            for(int i = 0; i < nodes.getLength(); i++)
            {
                Node node = nodes.item(i);

                if(node.getNodeName().equals("features"))
                {
                    experiment.setFeatures(
                        ((Element)node).getAttribute("indices"));
                }
                else if(node.getNodeName().equals("pre_processors"))
                {
                    processPreProcessors((Element)node, experiment);
                }
                else if(node.getNodeName().equals("post_cache_pre_processors"))
                {
                    processPostCachePreProcessors((Element)node, experiment);
                }
                else if(node.getNodeName().equals("post_processors"))
                {
                    processPostProcessors((Element)node, experiment);
                }
                else
                {
                    if(dataProviderParsers.containsKey(node.getNodeName()))
                    {
                        IDataProvider provider = dataProviderParsers.get(
                                node.getNodeName()).parse((Element)node);

                        experiment.setDataProvider(provider);
                    }
                    else if(classifierParsers.containsKey(node.getNodeName()))
                    {
                        experiment.setClassifier(classifierParsers.get(
                                    node.getNodeName()).parse((Element)node));
                    }
                }
            }

            return experiment;

        }
        catch(Exception e)
        {
            System.out.println("Exception in Parsing Experiment");
            System.out.println(e);
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
            return null;
        }
    }

    private void processDataLoaders(Element element, DataCache cache)
    {
        NodeList nodes = element.getChildNodes(); 
        for(int i = 0; i < nodes.getLength(); i++)
        {
            Node node = nodes.item(i);

            if(dataLoaderParsers.containsKey(node.getNodeName()))
            {
                cache.addDataLoader(dataLoaderParsers.get(
                            node.getNodeName()).parse((Element)node));
            }
        }
    }

    private void processPreProcessors(Element element, Experiment exp)
    {
        NodeList nodes = element.getChildNodes(); 
        for(int i = 0; i < nodes.getLength(); i++)
        {
            Node node = nodes.item(i);
            if(preProcessorParsers.containsKey(node.getNodeName()))
            {
                exp.addPreProcessor(preProcessorParsers.get(
                            node.getNodeName()).parse((Element)node));
            }
        }
    }

    private void processPostCachePreProcessors(Element element, Experiment exp)
    {
        NodeList nodes = element.getChildNodes(); 
        for(int i = 0; i < nodes.getLength(); i++)
        {
            Node node = nodes.item(i);
            if(preProcessorParsers.containsKey(node.getNodeName()))
            {
                exp.addPreProcessor(preProcessorParsers.get(
                            node.getNodeName()).parse((Element)node));
            }
        }
    }

    private void processPostProcessors(Element element, Experiment experiment)
    {
        NodeList nodes = element.getChildNodes(); 
        for(int i = 0; i < nodes.getLength(); i++)
        {
            Node node = nodes.item(i);
            if(postProcessorParsers.containsKey(node.getNodeName()))
            {
                experiment.addPostProcessor(postProcessorParsers.get(
                            node.getNodeName()).parse((Element)node));
            }
        }
    }
}
